package com.elli0tt.session;

import com.elli0tt.pojo.BoundSql;
import com.elli0tt.pojo.Configuration;
import com.elli0tt.pojo.MappedStatement;
import com.elli0tt.utils.GenericTokenParser;
import com.elli0tt.utils.ParameterMapping;
import com.elli0tt.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author elli0tt
 * @version 1.0
 * @className SimpleExecutor.java
 * @description 简单执行器
 * @createTime 2020-08-28 23:29:51
 */
public class SimpleExecutor implements Executor {


    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            // 1. 注册驱动，获取数据库连接
            connection = configuration.getDataSource().getConnection();
            configuration.setAutoCommit(configuration.isAutoCommit());
            // 2. 获取 sql 语句，转换 sql 语句
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
            // 3. 预处理对象
            preparedStatement = connection.prepareStatement(boundSql.getSqlText());

            Class<?> parameterType = mappedStatement.getParameterType();
            // 4. 设置参数
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                String fieldName = parameterMapping.getContent();
                Field declaredField = parameterType.getDeclaredField(fieldName);
                declaredField.setAccessible(true);
                Object object = declaredField.get(params[0]);
                preparedStatement.setObject(i + 1, object);
            }

            // 5. 执行 sql
            resultSet = preparedStatement.executeQuery();
            Class<?> resultType = mappedStatement.getResultType();

            List<E> resultList = new ArrayList<>();
            // 6. 封装结果集
            while (resultSet.next()) {
                E resultObject = (E) resultType.newInstance();
                // 元数据信息
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 0; i < metaData.getColumnCount(); i++) {
                    String columnName = metaData.getColumnName(i + 1);
                    Object columnValue = resultSet.getObject(columnName);
                    // 内省
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultType);
                    // setter
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(resultObject, columnValue);
                }
                resultList.add(resultObject);
            }
            return resultList;
        } finally {
            closeResultSet(resultSet);
            closeStatement(preparedStatement);
            closeConnection(connection);
        }
    }

    @Override
    public int update(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = configuration.getDataSource().getConnection();
            configuration.setAutoCommit(configuration.isAutoCommit());
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
            preparedStatement = connection.prepareStatement(boundSql.getSqlText());

            Class<?> parameterType = mappedStatement.getParameterType();
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                String fieldName = parameterMapping.getContent();
                Field declaredField = parameterType.getDeclaredField(fieldName);
                declaredField.setAccessible(true);
                Object object = declaredField.get(params[0]);
                preparedStatement.setObject(i + 1, object);
            }
            return preparedStatement.executeUpdate();
        } finally {
            closeStatement(preparedStatement);
            closeConnection(connection);
        }
    }

    /**
     * 获取处理占位符以及提取参数名称列表后的 sql 对象
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        // 标记处理类: 配置标记处理器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        // 标记解析器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // 解析出来的sql
        String parseSql = genericTokenParser.parse(sql);
        // #{} 里解析出来的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        return new BoundSql(parseSql, parameterMappings);
    }

    private void closeStatement(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private void closeResultSet(ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private void closeConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
